import { convertTo2DArray } from "@/utils";
// prettier-ignore
import { AutoComplete, AutoCompleteProps, Button, Col, Form, FormInstance, FormItemProps, FormProps, Input, InputNumber, InputNumberProps, InputProps, Radio, RadioGroupProps, Row, Select, Space, SpaceProps, TableColumnType } from "antd";
import FormItem from "antd/es/form/FormItem";
import { TextAreaProps } from "antd/es/input";
import { DefaultOptionType } from "antd/es/select";
import { SelectProps } from "antd/lib";
import {
    ComponentType,
    forwardRef,
    HTMLAttributes,
    ReactNode,
    Ref,
    useCallback,
    useEffect,
    useImperativeHandle,
    useMemo,
    useRef,
    useState,
} from "react";
import styles from "./FormByData.module.scss";
import DictionaryFormItem, { DictionaryFormItemProps } from "../DictionaryFormItem";
import AuthButton from "@/components/Button";

export type FormTableItemType<D = any> = Partial<FormItemType<D>> & TableColumnType<D> & { isForm?: boolean; isTable?: boolean };

type RecursivePartial<T> = NonNullable<T> extends object
    ? {
        [P in keyof T]?: NonNullable<T[P]> extends (infer U)[]
        ? RecursivePartial<U>[]
        : NonNullable<T[P]> extends object
        ? RecursivePartial<T[P]>
        : T[P];
    }
    : T;

export const formatFormItem = <D = any,>(columns: FormTableItemType<D>[]): FormItemType<D>[] =>
    columns
        .filter(({ isForm }) => isForm !== false)
        .map(({ dataIndex, title, render, ...item }) => ({ label: title as ReactNode, name: dataIndex as FormItemType["name"], ...item }));

export type FormItemType<D = any> = FormItemProps<D> & {
    name: keyof D | string;
    options?: (Omit<DefaultOptionType, "value"> & { value?: string | number | boolean })[];
    type?: "textarea" | "inputNumber" | "radio" | "select" | "autoComplete" | 'input';
    dictType?: string;
    dictClass?: "number" | "boolean" | "string";
    component?: ComponentType<any>;
    placeholder?: string;
    allowClear?: boolean;
    formProps?: FormProps;
    componentProps?: SelectProps | InputProps | TextAreaProps | Record<string, any>;
    suffix?: JSX.Element;
    wrapStyle?: HTMLAttributes<unknown>["style"];
    isForm?: boolean;
    isTable?: boolean;
    minWidth?: TableColumnType["minWidth"];
};

export interface FormByDataProps<D> {
    formItems: FormItemType<D>[];
    cols?: number;
    footer?: ReactNode;
    getFormRef?: (ref: FormInstance<D>) => void;
    onSubmit?: (formData: D) => void;
    onCancel?: () => void;
    onValuesChange?: (changedValues: any, values: any) => void;
    formProps?: FormProps;
    rowClass?: HTMLAttributes<unknown>["className"];
    footerPlacement?: "left" | "center" | "right";
    actionMore?: ReactNode;
    okText?: string;
    footerClass?: HTMLAttributes<unknown>["className"];
    spaceProps?: SpaceProps;
    required?: boolean;
    submitPermission?: string;
}

export interface FormByDataRef<D> {
    validate: () => Promise<D>;
    submit: () => Promise<void>;
    getFieldsValue: () => D;
    setFieldsValue: (obj: RecursivePartial<D>) => void;
}

const FormByData = forwardRef(<D = Record<string, any>,>(props: FormByDataProps<D>, ref: Ref<FormByDataRef<D>>) => {
    const {
        formItems,
        cols = 3,
        footer,
        getFormRef,
        onSubmit,
        onCancel,
        onValuesChange,
        formProps,
        rowClass,
        footerPlacement = "center",
        actionMore,
        okText,
        footerClass = "",
        spaceProps,
        required,
        submitPermission,
    } = props;
    const list = convertTo2DArray(formItems, cols);
    const formRef = useRef<FormInstance<D>>(null);
    const [submitLoading, setSubmitLoading] = useState(false);

    const getPlaceholder = (formItem: FormItemType) => {
        const { label, options, type, component, placeholder } = formItem;
        if (placeholder) return placeholder;
        if (type === "autoComplete") return `请选择或输入${label}`;
        if (type === "select" || options || component?.name?.toLowerCase().includes("select")) return `请选择${label}`;
        return `请输入${label}`;
    };

    const renderFormItemChild = useCallback(
        (formItem: FormItemType) => {
            const { componentProps = {}, placeholder, allowClear, suffix, wrapStyle } = formItem;
            placeholder && Object.assign(componentProps, { placeholder });
            allowClear !== undefined && Object.assign(componentProps, { allowClear });
            let component = <Input placeholder={getPlaceholder(formItem)} {...(componentProps as InputProps)} />;
            if (formItem.type === "radio" && formItem.options) {
                component = (
                    <Radio.Group {...(componentProps as RadioGroupProps)}>
                        {formItem.options.map((item, index) => (
                            <Radio value={item.value} key={index}>
                                {item.label}
                            </Radio>
                        ))}
                    </Radio.Group>
                );
            }
            if (formItem.type === "inputNumber") {
                component = <InputNumber className="w-full" placeholder={getPlaceholder(formItem)} {...(componentProps as InputNumberProps)} />;
            }
            if (formItem.type === "radio") {
                component = (
                    <Radio.Group {...(componentProps as RadioGroupProps)}>
                        <Radio value={true}>是</Radio>
                        <Radio value={false}>否</Radio>
                    </Radio.Group>
                );
            }
            if (formItem.type === "autoComplete") {
                component = (
                    <AutoComplete
                        placeholder={getPlaceholder(formItem)}
                        options={(formItem.options as AutoCompleteProps["options"]) || []}
                        {...(componentProps as SelectProps)}
                    />
                );
            }
            if (!["autoComplete", "radio"].includes(formItem.type!) && formItem.options) {
                component = (
                    <Select placeholder={getPlaceholder(formItem)} {...(componentProps as SelectProps)}>
                        {formItem.options.map((item, index) => (
                            <Select.Option key={index} value={item.value}>
                                {item.label}
                            </Select.Option>
                        ))}
                    </Select>
                );
            }
            if (formItem.type === "textarea") {
                component = (
                    <Input.TextArea placeholder={getPlaceholder(formItem)} style={{ resize: "none" }} {...(componentProps as TextAreaProps)} />
                );
            }
            if (formItem.type === "inputNumber") {
                component = <InputNumber className="w-full" placeholder={getPlaceholder(formItem)} {...(componentProps as InputNumberProps)} />;
            }
            if (formItem.component) {
                component = <formItem.component placeholder={getPlaceholder(formItem)} {...componentProps} />;
            }
            if (formItem.dictType && ["radio", "select", undefined].includes(formItem.type)) {
                component = (
                    <DictionaryFormItem
                        {...(componentProps as DictionaryFormItemProps)}
                        dictType={formItem.dictType}
                        dictClass={formItem.dictClass || "string"}
                        componentType={(formItem.type || "select") as DictionaryFormItemProps["componentType"]}
                        placeholder={getPlaceholder(formItem)}
                    />
                );
            }
            if (wrapStyle)
                return (
                    <div style={wrapStyle}>
                        {component}
                        {suffix}
                    </div>
                );
            return (
                <>
                    {component}
                    {suffix}
                </>
            );
        },
        [formItems]
    );

    const expandFormItemProps = ({
        name,
        options,
        type,
        placeholder,
        allowClear,
        component,
        componentProps,
        rules,
        wrapStyle,
        dictClass,
        dictType,
        isTable,
        isForm,
        minWidth,
        ...remainingProps
    }: FormItemType) => remainingProps || {};

    useEffect(() => {
        formRef.current && getFormRef?.(formRef.current);
    }, []);

    const submit = async () => {
        setSubmitLoading(true);
        try {
            const formData = await formRef.current?.validateFields();
            await onSubmit?.(formData!);
        } finally {
            setSubmitLoading(false);
        }
    };
    const footerPlacementMap = {
        left: "flex-start",
        center: "justify-center",
        right: "flex-end",
    };
    const SubmitButton = useMemo(() => (submitPermission ? AuthButton : Button), [submitPermission]);
    useImperativeHandle(ref, () => ({
        validate: () => formRef.current!.validateFields(),
        submit,
        getFieldsValue: () => formRef.current!.getFieldsValue(),
        setFieldsValue: (obj: RecursivePartial<D>) => formRef.current!.setFieldsValue(obj),
    }));
    return (
        <Form
            ref={formRef}
            className={`justify-between ${styles.formByData}`}
            labelCol={{ span: 6 }}
            wrapperCol={{ span: 17, offset: 1 }}
            onValuesChange={onValuesChange}
            {...formProps}
        >
            {list.map((formItems, formItemsIndex) => (
                <Row className={`w-full ${rowClass}`} key={formItemsIndex}>
                    {formItems.map((formItem, formItemIndex) => (
                        <Col flex="1" key={`${formItemsIndex}-${formItemIndex}`}>
                            <FormItem
                                name={formItem.name as string}
                                rules={required || formItem.required ? [{ required: true, message: `请输入${formItem.label}` }] : formItem.rules}
                                {...expandFormItemProps(formItem)}
                            >
                                {renderFormItemChild(formItem)}
                            </FormItem>
                        </Col>
                    ))}
                    {Array.from({ length: cols - formItems.length }).map((_, index) => (
                        <Col flex="1" key={`empty-${formItemsIndex}-${index}`} />
                    ))}
                </Row>
            ))}
            {footer || (
                <Row className={`mt-3 ${footerPlacementMap[footerPlacement]} w-full ${rowClass} ${footerClass}`}>
                    <Space rootClassName="pl-2" {...spaceProps}>
                        {onSubmit && (
                            <SubmitButton
                                {...(submitPermission ? { permission: submitPermission } : {})}
                                loading={submitLoading}
                                type="primary"
                                onClick={submit}
                            >
                                {okText || "提交"}
                            </SubmitButton>
                        )}
                        {onCancel && (
                            <Button
                                onClick={() => {
                                    formRef.current?.resetFields();
                                    onCancel?.();
                                }}
                            >
                                取消
                            </Button>
                        )}
                        {actionMore}
                    </Space>
                </Row>
            )}
        </Form>
    );
});

export default FormByData;
